/*
                                                         --> 方法名
    rpc_rounter --> 进行服务管理 -- service_desc          --> 参数 + 格式描述
                                                         --> 参数校验
                                                         --> 方法回调函数
*/
#pragma once
#include "../common/net.hpp"
#include "../common/message.hpp"

// 1.枚举类: 参数类型
// 2.服务描述信息类: (方法名 + 业务回调 + (字段,类型) + 返回值类型 + 参数校验)
// 3.服务管理类: 管理多种服务
// 4.对外的RpcRouter类: 服务注册, 提供给dispatcher的rpc请求处理回调
namespace jsonrpc
{
    // 客户端和服务端的接口可能重合
    namespace server
    {
        // 定义json中的数据类型         1         2       3       4      5
        enum class VType{ BOOL = 0, INTEGRAL, NUMERIC, STRING, ARRAY, OBJECT};

        // 服务描述
        class ServiceDescribe
        {
        public:
            using ptr = std::shared_ptr<ServiceDescribe>;
            using ServiceCallBack = std::function<void(const Json::Value&, Json::Value&)>;
            using ParamsDescribe = std::pair<std::string, VType>;

            // 构造函数 -- 下面通过build的时候直接传递属性
            ServiceDescribe(std::string&& method_name, std::vector<ParamsDescribe>&& param_desc, 
            ServiceCallBack&& callback, VType return_type)
            :_method_name(std::move(method_name))
            ,_param_desc(std::move(param_desc))
            ,_callback(std::move(callback))
            ,_return_type(return_type)
            {}
            
            // 检查参数字段是否存在, 类型是否一致
            bool paramCheck(const Json::Value& params)
            {
                //DLOG("调用paramCheck, 检查类型");
                for (auto& desc : _param_desc)
                {
                    //DLOG("调用params.isMember(desc.first), 检查字段是否匹配");
                    std:: cout << desc.first << std::endl;
                    
                    if(params.isMember(desc.first) == false)
                    {
                        ELOG("参数缺失字段: %s", desc.first.c_str());
                        return false;
                    }

                    if(check(desc.second, params[desc.first]) == false)
                    {
                        ELOG("参数字段类型不匹配: %s", desc.first.c_str());
                        return false; 
                    }
                }
                return true;
            }

            // 调用回调函数 + 拿到结果
            bool call(const Json::Value& params, Json::Value& result)
            {
                _callback(params, result);
                if(rtcheck(result) == false)
                {
                    ELOG("返回类型与设置的类型不相同");
                    return false;
                }
                return true;
            }

            const std::string& method(){return _method_name;}

        private:
            // 检查返回类型
            bool rtcheck(const Json::Value& val)
            {
                return check(_return_type, val);
            }
            // 根据val检查类型是否匹配
            bool check(VType vtype , const Json::Value& val)
            {
                //DLOG("根据val检查类型是否匹配");
                switch (vtype)
                {
                    case VType::BOOL : return val.isBool();
                    case VType::INTEGRAL : return val.isIntegral();
                    case VType::NUMERIC : return val.isNumeric();
                    case VType::STRING : return val.isString();
                    case VType::ARRAY : return val.isArray();
                    case VType::OBJECT : return val.isObject();
                }
                return false;
            }

        private:
            std::string _method_name;   // 方法名称
            ServiceCallBack _callback;  // 业务回调函数
            std::vector<ParamsDescribe>  _param_desc; // 参数字段格式描述
            VType _return_type;         // 返回值类型的描述
        };


        // ServiceDescribe工厂  
        // 建造者模式(做校验要用到对应数据, 创建的时候就先设置好 -- 不然在上面对外提供接口去设置属性, 就需要考虑线程安全问题)
        class SDescribeFactory
        {
        public:
            // 设置属性
            void setMethod(const std::string method){ _method_name = method;}
            void setServiceCallBack(const ServiceDescribe::ServiceCallBack& cb){ _callback = cb;}
            void setReturenType(VType type){ _return_type = type; }
            void setParamsDescribe(const std::string& name, VType type)
            {
                _param_desc.push_back(ServiceDescribe::ParamsDescribe(name, type));
            }

            // 提供创建ParaDesc的接口
            ServiceDescribe::ptr build()
            {
                return std::make_shared<ServiceDescribe>(std::move(_method_name), std::move(_param_desc),
                std::move(_callback), _return_type);
            }

        private:
            std::string _method_name;   // 方法名称
            ServiceDescribe::ServiceCallBack _callback;  // 业务回调函数
            std::vector<ServiceDescribe::ParamsDescribe>  _param_desc; // 参数字段格式描述
            VType _return_type;   // 返回值类型的描述
        };

        // 服务管理
        class ServiceManager
        {
        public:
            using ptr = std::shared_ptr<ServiceManager>;
            // 插入新服务
            void insert(const ServiceDescribe::ptr& desc)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _services.insert(std::make_pair(desc->method(), desc));
            }
            // 查询服务
            ServiceDescribe::ptr select(const std::string& method)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _services.find(method);
                if(it == _services.end())
                {
                    ELOG("未找到该服务...");
                    return ServiceDescribe::ptr();
                }
                return it->second;
            }

            // 删除服务
            void remove(const std::string& method)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _services.erase(method);
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, ServiceDescribe::ptr> _services;
        };

        // 管理服务, 收到请求-->检查-->调用服务, 返回响应
        class RpcRouter
        {
        public:
            using ptr = std::shared_ptr<RpcRouter>;
            RpcRouter(): _service_manager(std::make_shared<ServiceManager>()){}

            // 注册到Dispatcher模块, 对rpc请求进行回调处理的业务函数
            void onRpcRequest(const BaseConnection::ptr& conn, RpcRequest::ptr& req) 
            {
                //DLOG("开始调用onRpcRequest");
                // 1.能否提供服务
                auto Service = _service_manager->select(req->method());
                if(Service.get() == nullptr)
                {
                    ELOG("无法提供该服务, %s", req->method().c_str());
                    return response(conn, req, Json::Value(), RCode::RCODE_NOT_FOUND_SERVICE);
                }

                //DLOG("开始校验是参数类型否通过"); -- 括号位置写错导致的bug....
                // 2.校验是否通过
                if(Service->paramCheck(req->parameters())  == false)
                {
                    ELOG("参数校验失败, %s", req->method().c_str());
                    return response(conn, req, Json::Value(), RCode::RCODE_INVALID_PARAMS);
                }

                //DLOG("开始调用回调函数");
                // 3.调用回调函数 
                Json::Value result;
                bool ret = Service->call(req->parameters(), result);
                if(ret == false)
                {
                    ELOG("返回类型不正确, %s", req->method().c_str());
                    return response(conn, req, Json::Value(), RCode::RCODE_INTERNAL_ERROR);
                }

                DLOG("返回类型正确, 发送结果");   
                // 4.根据结果构建响应并发送
                return response(conn, req, result, RCode::RCODE_OK);
            }

            // 把服务注册到ServiceManger里
            void registerMethod(const ServiceDescribe::ptr& service)
            {
                return _service_manager->insert(service);
            }
        private:
            // 发送响应
            void response(const BaseConnection::ptr &conn, RpcRequest::ptr& req, const Json::Value& ret, RCode rcode)
            {
                auto msg = MessageFactory::create<RpcResponse>();
                msg->setId(req->rid());
                msg->setMType(jsonrpc::MType::RSP_RPC);
                msg->setRCode(rcode);
                msg->setResult(ret);
                conn->send(msg);
            }
        private:
            ServiceManager::ptr _service_manager;
        };
    }
}
